package utils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * 将object[] 转化为bean，配合handler使用
 */
public class ArrayBean {

    private Class type;//bean的class类型
    private PropertyDescriptor[] props = null;//bean的属性数组
    private int[] columnToProperty = null;//一个映射，object[]的序号 对应 bean的哪些属性
    //比如columnToProperty[1]=3,表示object[1]对应props数组的第3个
    //tobean的时候先循环的是columnToProperty

    private int start = 1;//object[]的开始位置
    private int size = 1;//object[]的操作的大小范围

    public void setDefault(Class type, int start, int size, Map<Integer, String> meta) {
        this.type = type;
        this.size = size;
        this.start = start;
        props = this.propertyDescriptors(type);
        //列的名字信息
        columnToProperty = this.mapColumnsToProperties(start, size, meta);
    }


    /**
     * 转化为bean
     *
     * @param obj Object[]
     * @return bean
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws SQLException
     */
    public Object toBean(Object[] obj)
            throws InstantiationException, IllegalAccessException, SQLException {
        Object bean = this.newInstance(type);
        //从start开始，循环size次
        for (int i = start; i < start + size; i++) {

            if (columnToProperty[i] == PROPERTY_NOT_FOUND) {
                continue;//在bean中找不到对应的属性可以设置，所以跳过
            }

            PropertyDescriptor prop = props[columnToProperty[i]];
            Class propType = prop.getPropertyType();//bean的属性的类型，如string，int

            Object value = obj[i - 1];//要设置的数据

            if (propType != null && value == null && propType.isPrimitive()) {
                //如果数据查询没有查到该项的数据
                //而且bean属性是基础类型，
                // 那么初始化基础类型
                value = primitiveDefaults.get(propType);
            }

            this.callSetter(bean, prop, value);
        }

        return bean;
    }

    private PropertyDescriptor[] propertyDescriptors(Class c) {
        // Introspector caches BeanInfo classes for better performance
        BeanInfo beanInfo = null;
        try {
            beanInfo = Introspector.getBeanInfo(c);
        } catch (IntrospectionException e) {
            e.printStackTrace();
        }

        return beanInfo != null ? beanInfo.getPropertyDescriptors() : new PropertyDescriptor[0];
    }

    protected static final int PROPERTY_NOT_FOUND = -1;

    //
    protected int[] mapColumnsToProperties(int start, int size, Map<Integer, String> meta) {

        int total = meta.size();
        int columnToProperty[] = new int[total + 1];
        Arrays.fill(columnToProperty, PROPERTY_NOT_FOUND);

        for (int col = start; col < start + size; col++) {
            String columnName = meta.get(col);
            for (int i = 0; i < props.length; i++) {

                if (columnName.equalsIgnoreCase(props[i].getName())) {
                    columnToProperty[col] = i;
                    break;
                }
            }
        }

        return columnToProperty;
    }

    //创建新实例
    protected Object newInstance(Class c) throws IllegalAccessException, InstantiationException {
        return c.newInstance();
    }

    private static final Map<Object, Object> primitiveDefaults = new HashMap<Object, Object>();

    static {
        primitiveDefaults.put(Character.TYPE, '\u0000');
        primitiveDefaults.put(Integer.TYPE, 0);
        primitiveDefaults.put(Short.TYPE, (short) 0);
        primitiveDefaults.put(Byte.TYPE, (byte) 0);
        primitiveDefaults.put(Float.TYPE, 0f);
        primitiveDefaults.put(Double.TYPE, 0d);
        primitiveDefaults.put(Long.TYPE, 0L);
        primitiveDefaults.put(Boolean.TYPE, Boolean.FALSE);
    }

    /**
     * 调用setter设置值
     *
     * @param target 调用方法的类
     * @param prop   能获取到类的setter
     * @param value  要设置的值
     */
    private void callSetter(Object target, PropertyDescriptor prop, Object value) {

        Method setter = prop.getWriteMethod();

        if (setter == null) {
            return;
        }

        Class[] params = setter.getParameterTypes();
        try {
            // datetime从数据库返回的是timestamp的类型的object
            // 又因为timestamp是Date的子类
            // 所以可以把它们相互转换为相同的类型
            if (value != null) {
                if (value instanceof java.util.Date) {
                    if (params[0].getName().equals("java.sql.Date")) {
                        value = new java.sql.Date(((java.util.Date) value).getTime());
                    } else if (params[0].getName().equals("java.sql.Time")) {
                        value = new java.sql.Time(((java.util.Date) value).getTime());
                    } else if (params[0].getName().equals("java.sql.Timestamp")) {
                        value = new java.sql.Timestamp(((java.util.Date) value).getTime());
                    }
                }
            }

            // Don't call setter if the value object isn't the right type
            if (this.isCompatibleType(value, params[0])) {
                setter.invoke(target, value);
            } else {
                System.out.println("类型不兼容");
            }

        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * Method.invoke() 可以接受interger类型到int
     *
     * @param value 传给 setter 方法的值.
     * @param type  setter 方法的参数.
     * @return 如果类型兼容就返回 boolean True.
     */
    private boolean isCompatibleType(Object value, Class type) {
        // 先看看类型是否匹配，如int和integer就不匹配，但它们是兼容类型，所以往下继续判断
        if (value == null || type.isInstance(value)) {
            return true;

        } else if (
                type.equals(Integer.TYPE) && Integer.class.isInstance(value)) {
            return true;

        } else if (type.equals(Long.TYPE) && Long.class.isInstance(value)) {
            return true;

        } else if (
                type.equals(Double.TYPE) && Double.class.isInstance(value)) {
            return true;

        } else if (type.equals(Float.TYPE) && Float.class.isInstance(value)) {
            return true;

        } else if (type.equals(Short.TYPE) && Short.class.isInstance(value)) {
            return true;

        } else if (type.equals(Byte.TYPE) && Byte.class.isInstance(value)) {
            return true;

        } else if (
                type.equals(Character.TYPE) && Character.class.isInstance(value)) {
            return true;

        } else if (
                type.equals(Boolean.TYPE) && Boolean.class.isInstance(value)) {
            return true;

        } else {
            return false;
        }

    }

}
